﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using DimensionsHelper.Common.Contracts.Table;
using DimensionsHelper.Common.Table;

namespace DimensionsHelper.Services.CsvReader;

[DebuggerDisplay("OriginalIndex = {OriginalIndex}, First = {FirstNotEmptyCell}")]
public class CsvLine : ILine
{
    private readonly List<ICell> _cells;
    private ILine? _lastLine;

    internal CsvLine(int originalIndex, ILine? lastLine = null)
    {
        OriginalIndex = originalIndex;
        _cells = [];
        LineType = LineType.Normal;
        NotEmptyCellCount = 0;
        _lastLine = lastLine;
        if (lastLine != null)
        {
            ((CsvLine)lastLine).NextLine = this;
        }
    }

    public ICell this[int index] => _cells[index];

    public int OriginalIndex { get; }

    public int Count => _cells.Count;

    public int NotEmptyCellCount { get; private set; }

    public object[] Values => _cells.Select(c => c.Value).ToArray();

    public object[,] SelectValues(Func<object, object> callback)
    {
        var values = new object[1, Count];

        for (var i = 0; i < Count; i++)
        {
            values[0, i] = callback(_cells[i].Value);
        }

        return values;
    }

    public LineType LineType { get; internal set; }

    public ILine? PreviousLine
    {
        get => _lastLine;
        internal set
        {
            if (_lastLine != null)
            {
                ((CsvLine)_lastLine).NextLine = null;
            }

            _lastLine = value;

            if (value != null)
            {
                ((CsvLine)value).NextLine = this;
            }
        }
    }

    public ILine? NextLine { get; private set; }

    public ILine? RelativeSignificantTestLine { get; internal set; }

    public ITable? Table { get; internal set; }

    public ICell? FirstNotEmptyCell => _cells.FirstOrDefault(cell => !cell.IsEmpty);

    public ICell Add()
    {
        var cell = new CsvCell(this, Count);
        cell.ValueChanged += OnCsvCellValueChanged;
        _cells.Add(cell);
        return cell;
    }

    public ICell Add(string text)
    {
        var cell = Add();
        cell.Value = text;
        return cell;
    }

    public IEnumerator<ICell> GetEnumerator()
    {
        return _cells.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _cells.GetEnumerator();
    }

    public bool IsEmpty => NotEmptyCellCount == 0;

    public bool IsFullFilled => NotEmptyCellCount == _cells.Count;

    public void RTrim()
    {
        while (_cells.Count > 0 && _cells[^1].IsEmpty)
        {
            _cells.RemoveAt(_cells.Count - 1);
        }
    }

    public bool TestLineType(LineType lineType)
    {
        return (lineType & LineType) > 0;
    }

    private void OnCsvCellValueChanged(object? sender, CellValueChangedArgs args)
    {
        var isNewTextEmpty = string.IsNullOrEmpty(args.NewText);
        var isOldTextEmpty = string.IsNullOrEmpty(args.OldText);

        if (isOldTextEmpty && !isNewTextEmpty)
        {
            NotEmptyCellCount++;
        }
        else if (!isOldTextEmpty && isNewTextEmpty)
        {
            NotEmptyCellCount--;
        }
    }
}